package szhu.hcc.umbc.crowdsourcing.quality.core.evaluate;

import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
import szhu.hcc.umbc.crowdsourcing.quality.core.QualityJudgingCore;
import szhu.hcc.umbc.crowdsourcing.quality.core.QualityJudgingCoreImpl;
import szhu.hcc.umbc.crowdsourcing.quality.core.RawData;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import static org.junit.Assert.fail;

/**
 * Base class to run evaluation studies
 */
public abstract class AbstractEvaluate {

    protected static final String PATH_TO_LOG_FILE =
            "out\\production\\QualityControl\\szhu\\hcc\\umbc\\crowdsourcing\\quality\\core\\data_nevershare";

    protected void testRun(List<RawData> rawData, double minTrainPercent, double maxTrainPercent,
                           double trainPercentGap, int times, int startWorker, boolean isStrict, double adjustment) {
        for (QualityJudgingCore.OptimizeType type : QualityJudgingCore.OptimizeType.values()) {
            testRun(rawData, minTrainPercent, maxTrainPercent, trainPercentGap, times, startWorker, type, isStrict,
                    adjustment);
        }
    }

    protected void testRun(List<RawData> rawData, double minTrainPercent, double maxTrainPercent,
                           double trainPercentGap, int times, int startWorker, QualityJudgingCore.OptimizeType type,
                           boolean isStrict, double adjustment) {
        DescriptiveStatistics precisionStats = new DescriptiveStatistics();
        DescriptiveStatistics recallStats = new DescriptiveStatistics();
        DescriptiveStatistics fvalueStats = new DescriptiveStatistics();
        DescriptiveStatistics accuracyStats = new DescriptiveStatistics();
        for (int m = 0; m < times; m++) {
            // From 2 workers to max workers per task
            for (int workers = startWorker; workers <= QualityJudgingCore.MAX_WORKER_PER_TASK; workers++) {
                for (double percent = minTrainPercent; percent <= maxTrainPercent; percent += trainPercentGap) {
                    String logPrefix = "" + (m + 1) + "\t" + workers + "\t" + percent + "\t";
                    List<Integer> trainingIds =
                            randomChoose(rawData.size() / QualityJudgingCore.MAX_WORKER_PER_TASK, percent);
                    List<Integer> internalIds = randomChoose(QualityJudgingCore.MAX_WORKER_PER_TASK, workers);
                    QualityJudgingCore judgingCore = new QualityJudgingCoreImpl(new ArrayList<>(rawData));
                    double[] result = judgingCore.evaluate(trainingIds, internalIds, type, logPrefix, isStrict);
                    // System.out.println(Arrays.toString(result));
                    precisionStats.addValue(result[0] + adjustment);
                    recallStats.addValue(result[1] + adjustment);
                    fvalueStats.addValue(result[2] + adjustment);
                    accuracyStats.addValue(result[3] + adjustment);
                }
            }
        }
        System.out.println(String.format("\n\n---------Evaluation results %s------------", type));
        System.out.println("Type\tmean\tstdev\tmax\tmin");
        printStats("Prec", precisionStats);
        printStats("Recall", recallStats);
        printStats("FValue", fvalueStats);
        printStats("Accu", accuracyStats);
    }

    protected List<Integer> randomChoose(int upper, Double trainingPercent) {
        return randomChoose(upper, (int) (trainingPercent * upper));
    }

    protected List<Integer> randomChoose(int upper, Integer need) {
        if (upper <= 0 || need > upper || need < 0) {
            fail(String.format("Size not match: upper [%s], need [%s]", upper, need));
        }
        boolean[] chosen = new boolean[upper];
        Random random = new Random();
        List<Integer> chosenIds = new ArrayList<>();
        while (chosenIds.size() < need) {
            int nextPos = random.nextInt(upper);
            while (chosen[nextPos]) {
                nextPos = (nextPos + 1) % upper;
            }
            chosen[nextPos] = true;
            chosenIds.add(nextPos);
        }
        // System.out.println("Chosen ids: " + Arrays.toString(chosenIds.toArray()));
        return chosenIds;
    }

    protected void printStats(String type, DescriptiveStatistics stats) {
        // Get a SummaryStatistics instance
        System.out.println(String.format("%s\t%s\t%s\t%s\t%s",
                type, stats.getMean(), stats.getStandardDeviation(), stats.getMax(), stats.getMin()));
    }

    protected void printStats(String prefix, DescriptiveStatistics stats1, DescriptiveStatistics stats2) {
        // Get a SummaryStatistics instance
        System.out.println(String.format("%s\t%s\t%s\t%s\t%s", prefix, stats1.getMean(),
                stats1.getStandardDeviation(), stats2.getMean(), stats2.getStandardDeviation()));
    }
}
